﻿using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Data.Objects;
using System.Linq;
using System.Text;
using DomainDriver.DomainModeling.DomainObjects;

namespace DomainDriver.DomainModeling.StorageManagers.EntityFrameworkStorage
{
    public static class IEfEntityUtils
    {
        #region CreateDefaultInstance Methods

        public static KEYED_DOMAIN_OBJECT CreateDefaultInstance<KEY, KEYED_DOMAIN_OBJECT>(this IStorageManager<KEY, KEYED_DOMAIN_OBJECT> storageManager)
            where KEYED_DOMAIN_OBJECT : class, IKeyedDomainObject<KEY, KEYED_DOMAIN_OBJECT>
        {
            return CreateDefaultInstance<KEY, KEYED_DOMAIN_OBJECT>();
        }

        public static KEYED_DOMAIN_OBJECT CreateDefaultInstance<KEY, KEYED_DOMAIN_OBJECT>()
            where KEYED_DOMAIN_OBJECT : class, IKeyedDomainObject<KEY, KEYED_DOMAIN_OBJECT>
        {
            var domainObject_Type = typeof(KEYED_DOMAIN_OBJECT);
            var domainObject_DefaultConstructor = domainObject_Type.GetConstructor(new Type[] { });

            if (domainObject_DefaultConstructor == null)
            { return null; }

            var domainObject_DefaultInstance = (KEYED_DOMAIN_OBJECT)domainObject_DefaultConstructor.Invoke(new object[] { });
            return domainObject_DefaultInstance;
        }

        public static IEfEntity_DbQueryable<KEY> CreateDefaultInstance_AsDbQueryable<KEY, KEYED_DOMAIN_OBJECT>(this IStorageManager<KEY, KEYED_DOMAIN_OBJECT> storageManager)
            where KEYED_DOMAIN_OBJECT : class, IKeyedDomainObject<KEY, KEYED_DOMAIN_OBJECT>
        {
            return CreateDefaultInstance_AsDbQueryable<KEY, KEYED_DOMAIN_OBJECT>();
        }

        public static IEfEntity_DbQueryable<KEY> CreateDefaultInstance_AsDbQueryable<KEY, KEYED_DOMAIN_OBJECT>()
            where KEYED_DOMAIN_OBJECT : class, IKeyedDomainObject<KEY, KEYED_DOMAIN_OBJECT>
        {
            var domainObject_DefaultInstance = CreateDefaultInstance<KEY, KEYED_DOMAIN_OBJECT>();

            if (domainObject_DefaultInstance == null)
            { return null; }
            if (!(domainObject_DefaultInstance is IEfEntity_DbQueryable<KEY>))
            { return null; }

            return (domainObject_DefaultInstance as IEfEntity_DbQueryable<KEY>);
        }

        #endregion

        #region Query-related Methods

        public static bool Supports_DbQueries<KEY, KEYED_DOMAIN_OBJECT>(this IStorageManager<KEY, KEYED_DOMAIN_OBJECT> storageManager)
            where KEYED_DOMAIN_OBJECT : class, IKeyedDomainObject<KEY, KEYED_DOMAIN_OBJECT>
        {
            return Supports_DbQueries<KEY, KEYED_DOMAIN_OBJECT>();
        }

        public static bool Supports_DbQueries<KEY, KEYED_DOMAIN_OBJECT>()
            where KEYED_DOMAIN_OBJECT : class, IKeyedDomainObject<KEY, KEYED_DOMAIN_OBJECT>
        {
            var domainObject_DefaultInstance = CreateDefaultInstance_AsDbQueryable<KEY, KEYED_DOMAIN_OBJECT>();
            return (domainObject_DefaultInstance != null);
        }

        public static string GetTableNameForEfEntity<KEYED_DOMAIN_OBJECT, STORED_KEY>(this DbContext dataContext)
            where KEYED_DOMAIN_OBJECT : class, IEfEntity<STORED_KEY>
        {
            var dbSet = dataContext.Set<KEYED_DOMAIN_OBJECT>();
            var setQuery = dataContext.Set<KEYED_DOMAIN_OBJECT>().ToString();
            var queryParts = setQuery.Split(new string[] { "FROM", "AS" }, StringSplitOptions.RemoveEmptyEntries);
            var tableNamePartIndex = (queryParts.Length - 2);
            var tableNamePartValue = queryParts[tableNamePartIndex];
            var defaultTableName = tableNamePartValue.Trim();

            var domainObjectType = typeof(KEYED_DOMAIN_OBJECT);
            var tableNameAttribute = domainObjectType.GetCustomAttributes(false).OfType<TableAttribute>().FirstOrDefault();
            var customTableName = (tableNameAttribute != null) ? tableNameAttribute.Name : string.Empty;

            var actualTableName = (!string.IsNullOrWhiteSpace(customTableName)) ? customTableName : defaultTableName;
            return actualTableName;
        }

        #endregion
    }
}